	//---------------------------------------------------------------------
	// Includes
	//---------------------------------------------------------------------			
	#include <sstream>	
	#include <iostream>
	#include <fstream>
	#include <Rendering/Shader.hpp>
	#include <Rendering/Renderer.hpp>
	
namespace Rendering {

	//---------------------------------------------------------------------
	// Shader Localizator methods
	//---------------------------------------------------------------------
	void ShaderLocalizator::AddLocation(const std::string& name, int location) {
		LocationDetail locDetail;
		locDetail.location	= location;
		locDetail.unit		= DEFAULT_UNIT;
		ressourceLocations[name] = locDetail;
	}
	//---------------------------------------------------------------------
	void ShaderLocalizator::AddLocation(const std::string& name, int location, int unit) {
		LocationDetail locDetail;
		locDetail.location	= location;
		locDetail.unit		= unit;
		ressourceLocations[name] = locDetail;
	}
	//---------------------------------------------------------------------
	bool ShaderLocalizator::FindLocation(const std::string& name, int& location) const {
		TLocationMap::const_iterator it;
		it = ressourceLocations.find(name);
		if(it!=ressourceLocations.end()){
			location = it->second.location;
			return true;
		}
		else
			return false;
	}
	//---------------------------------------------------------------------
	bool ShaderLocalizator::FindLocation(const std::string& name, int& location, int& unit) const {
		TLocationMap::const_iterator it;
		it = ressourceLocations.find(name);
		if(it!=ressourceLocations.end()){
			location = it->second.location;
			unit	 = it->second.unit;
			return true;
		}
		else
			return false;
	}
	//---------------------------------------------------------------------
	bool ShaderLocalizator::FindLocation(VertexBufferType type, int& location) const {
		return FindLocation(Enumeration::GetBufferType(type), location);
	}
	//---------------------------------------------------------------------
	void ShaderLocalizator:: GetAllLocation(TStringVector& names) const {
		TLocationMap::const_iterator it;
		for(it = ressourceLocations.begin(); it != ressourceLocations.end(); ++it) {
			names.push_back(it->first);
		}		
	}	

	
	//---------------------------------------------------------------------
	// Ressource Context methods
	//---------------------------------------------------------------------
	RessourceContext::RessourceContext():
	IShaderContext() {
	};
	//---------------------------------------------------------------------
	RessourceContext::~RessourceContext() {
	}
	//---------------------------------------------------------------------
	void RessourceContext::Enable() {
		int size = locations.size();
		for(int i =0;i<size;++i)
			locations[i]->Enable();
	}
	//---------------------------------------------------------------------
	void RessourceContext::Disable() {
		int size = locations.size();
		for(int i =0;i<size;++i)
			locations[i]->Disable();
	}
	//---------------------------------------------------------------------
	void RessourceContext::SetLocation(const ShaderLocalizator& localizator, ShaderCompilationReport& report) {
		int size = locations.size();
		for(int i =0;i<size;++i)
			locations[i]->SetLocation(localizator, report);			
	}
		
	
	
	//---------------------------------------------------------------------
	// Geometry Context methods
	//---------------------------------------------------------------------
	GeometryContext::GeometryContext(const Geometry::TMeshPointer& m, const Matrix::Matrix4& l2w) :
	IShaderContext(),
	mesh(m),
	localToWorldMatrix(l2w) {
	}
	//---------------------------------------------------------------------
	GeometryContext::~GeometryContext() {
	}
	//---------------------------------------------------------------------
	void GeometryContext::Enable() {		
		int nActive = activeBuffers.size();
		// Enable active buffers
		for(int i =0;i<nActive;++i)
			mesh->Enable(activeBuffers[i].type,activeBuffers[i].location);
		// Render mesh
		Renderer::Instance().PushMatrix();
		Renderer::Instance().MultMatrix(localToWorldMatrix);
		mesh->Render();
		Renderer::Instance().PopMatrix();
		// Disable active buffers
		for(int i =0;i<nActive;++i)
			mesh->Disable(activeBuffers[i].type,activeBuffers[i].location);
	}
	//---------------------------------------------------------------------
	void GeometryContext::Disable() {
		// Nothing to do
	}
	//---------------------------------------------------------------------
	void GeometryContext::SetLocation(const ShaderLocalizator& localizator, ShaderCompilationReport& report) {
		// Clear old active buffer
		activeBuffers.clear();
		
		// Get active buffer
		BufferLocation bufferLocation;		
		bool status;
		
		status = localizator.FindLocation(VERTEX_BUFFER, bufferLocation.location);
		if( status ) {
			bufferLocation.type = VERTEX_BUFFER;
			activeBuffers.push_back(bufferLocation);			
		}
		report.Report(Enumeration::GetBufferType(VERTEX_BUFFER), bufferLocation.location, status); 
			
		status = localizator.FindLocation(COLOR_BUFFER, bufferLocation.location);
		if( status ) {
			bufferLocation.type = COLOR_BUFFER;
			activeBuffers.push_back(bufferLocation);			
		}
		report.Report(Enumeration::GetBufferType(COLOR_BUFFER), bufferLocation.location, status);
						
		status = localizator.FindLocation(NORMAL_BUFFER, bufferLocation.location);
		if( status ) {
			bufferLocation.type = NORMAL_BUFFER;
			activeBuffers.push_back(bufferLocation);
		}
		report.Report(Enumeration::GetBufferType(NORMAL_BUFFER), bufferLocation.location, status);
		
		status = localizator.FindLocation(TEXCOORD0_BUFFER, bufferLocation.location);
		if( status ) {
			bufferLocation.type = TEXCOORD0_BUFFER;
			activeBuffers.push_back(bufferLocation);
		}
		report.Report(Enumeration::GetBufferType(TEXCOORD0_BUFFER), bufferLocation.location, status);
		
		status = localizator.FindLocation(TEXCOORD1_BUFFER, bufferLocation.location);
		if( status ) {
			bufferLocation.type = TEXCOORD1_BUFFER;
			activeBuffers.push_back(bufferLocation);
		}
		report.Report(Enumeration::GetBufferType(TEXCOORD1_BUFFER), bufferLocation.location, status);
		
		status = localizator.FindLocation(TEXCOORD2_BUFFER, bufferLocation.location);
		if( status ) {
			bufferLocation.type = TEXCOORD2_BUFFER;
			activeBuffers.push_back(bufferLocation);
		}
		report.Report(Enumeration::GetBufferType(TEXCOORD2_BUFFER), bufferLocation.location, status);
		
		status = localizator.FindLocation(TANGENT_BUFFER, bufferLocation.location);
		if( status ) {
			bufferLocation.type = TANGENT_BUFFER;
			activeBuffers.push_back(bufferLocation);
		}
		report.Report(Enumeration::GetBufferType(TANGENT_BUFFER), bufferLocation.location, status);
	}



	//---------------------------------------------------------------------
	// ShaderEnvironnement methods	
	//---------------------------------------------------------------------
	ShaderEnvironnement::ShaderEnvironnement(const TShaderProgramPointer& p ):
	program(p) {
	}
	//---------------------------------------------------------------------
	void ShaderEnvironnement::Add(ContextType type, const TIShaderContextPointer& context ) {
		contexts[type] = context;
	}
	//---------------------------------------------------------------------
	void ShaderEnvironnement::Compile(ShaderCompilationReport& report) {
		// Get locations from shader program
		ShaderLocalizator localizator;
		program->GetLocations(localizator);		
		// Assign location to each variable from each context
		for(int i =0;i<3;++i) {
			assert(contexts[i]!=NULL);
			contexts[i]->SetLocation(localizator, report);	
		}
		
		// Check if all shader variable are affected
		report.Check(localizator);
	}
	//---------------------------------------------------------------------
	TIShaderContextPointer& ShaderEnvironnement::GetContext(ContextType type) {
		return contexts[type];
	}
	//---------------------------------------------------------------------
	TShaderEnvironnementPointer ShaderEnvironnement::CreateEnvironnement( const TShaderProgramPointer& program ) {
		return TShaderEnvironnementPointer( new ShaderEnvironnement(program) );
	}
	//---------------------------------------------------------------------
	TShaderProgramPointer& ShaderEnvironnement::GetProgram() {
		return program;
	}



	//---------------------------------------------------------------------
	// Shader Program methods	
	//---------------------------------------------------------------------
	ShaderProgram::ShaderProgram(const std::string& vertexFilename, const std::string& fragmentFilename) {
		// Extract source from file
		std::stringstream vertexSource, fragmentSource;
		std::ifstream file;
		
		file.open(vertexFilename.c_str());
		assert(file!=0);
		vertexSource << file.rdbuf();
		file.close();
				
		file.open(fragmentFilename.c_str());
		assert(file!=0);	
		fragmentSource << file.rdbuf();;
		file.close();
		
		//std::cout << vertexSource.str() << std::flush;
		//std::cout << fragmentSource.str() << std::flush;
	
		programID = Renderer::Instance().CreateShader(vertexSource.str(), fragmentSource.str());
		assert(programID != 0);
	}		
	//---------------------------------------------------------------------	
	void ShaderProgram::Enable() const{
		Renderer::Instance().EnableShader(programID);
	}
	//---------------------------------------------------------------------	
	void ShaderProgram::Disable() const{
		Renderer::Instance().DisableShader(programID);
	}
	//---------------------------------------------------------------------	
	void ShaderProgram::GetLocations(ShaderLocalizator& localizator) const {
		Renderer::Instance().GetShaderLocations(programID, localizator);		
	}
	//---------------------------------------------------------------------	
	TShaderProgramPointer ShaderProgram::CreateShader(const std::string& vertexShader, const std::string& fragmentShader) {
		return TShaderProgramPointer(new ShaderProgram(vertexShader,fragmentShader));
	}
	//---------------------------------------------------------------------	
	TRenderingID ShaderProgram::GetRenderingID() const {
		return programID;
	}


	//---------------------------------------------------------------------
	// Shader Compilation Report methods	
	//---------------------------------------------------------------------
	void ShaderCompilationReport::Report(const std::string& name, int location, bool status) {
		Report(name, location, DEFAULT_UNIT, status);
	}
	//---------------------------------------------------------------------
	void ShaderCompilationReport::Report(const std::string& name, int location, int unit, bool status) {
		// Record variable description
		std::stringstream out;
		out << "Env->Shader : name(" << name << ") location("<< location << ") unit(" << unit << ") [" << (status?"OK":"ERROR") << "]";
		report.push_back(out.str());
		
		// Record variable name
		locationNames.push_back(name);
	}
	//---------------------------------------------------------------------
	void ShaderCompilationReport::Check(const ShaderLocalizator& localizator) {
		TStringVector locNames;
		localizator.GetAllLocation(locNames);
		
		// Check localizator names
		int size = locNames.size();
		for(int i=0;i<size;++i) {
				bool found = find(locationNames.begin(), locationNames.end(), locNames[i]) != locationNames.end();
				std::stringstream out;
				out << "Shader->Env : name(" << locNames[i] << ") [" << (found?"OK":"ERROR") << "]";
				report.push_back(out.str());
		}
	}
	//---------------------------------------------------------------------
	bool ShaderCompilationReport::IsCompile() const {
		return validCompilation;
	}
	//---------------------------------------------------------------------
	void ShaderCompilationReport::Print() const {
		// Build report
		std::stringstream out;
		out << "\n";
		out << "------------------------------------------------------------\n";
		out << " Compilation report\n";
		out << "------------------------------------------------------------\n";
		int size = report.size();
		for(int i=0;i<size;++i) {	
			out << report[i] << "\n";
		}
		// Print report
		Tools::Info(out.str().c_str());
	}
}
